#include <iostream>
#include "d_node.h"
#include "card.h"
#include "d_random.h"

using namespace std;


class DeckOfCards
{
   friend ostream& operator<<(ostream &ostr, const DeckOfCards &deck);

  public:
   DeckOfCards();
   ~DeckOfCards();
   void shuffle();
   card dealCard();
   bool isEmpty() const;

  private:
   node<card> *firstNode;
   void swapCards(int firstPosition, int secondPosition);
   int numberOfCards() const;
};


DeckOfCards::~DeckOfCards ()
{
   delete firstNode;
   firstNode=NULL;
}

DeckOfCards::DeckOfCards ()
//Start with the last node (ie assign next to NULL)
//Then build deck from top to bottom
{
   //Build all cards and keep track of previous node
   node<card> *prevNode;
   node<card> *currentNode;

   //prevNode starts as the last node in the linked list
   prevNode = NULL;

   //Make new card for each suit value (starting with Spades)
   for (int suit=4; suit>=1; suit--)
   {

      //Make new card for each face value (starting with Ace)
      for (int val=14; val>=2; val--)
      {
	 card mycard(val, suit);
	 //Point current node to prev node
	 currentNode = new node<card>(mycard, prevNode);
	 //Reset the prev node
	 prevNode = currentNode;
      }

   }

   //Finally, assign the private data to the first node
   firstNode = currentNode;

}

int DeckOfCards::numberOfCards() const
{
   int total=0;
   node<card> *currentNode;
   currentNode = firstNode;
   while (currentNode != NULL)
   {
      total++;
      currentNode = currentNode->next;
   }
   return total;
}


void DeckOfCards::swapCards(int firstPosition, int secondPosition)
{
   //find each card
   node<card> *currentNode = firstNode;
   node<card> *foundNode1, *foundNode2;
   node<card> *before1, *before2, *prevNode;

   int currentPosition=0;
   bool found1=false;
   bool found2=false;

      
   while (!found1 || !found2)
   {

      if (currentPosition == firstPosition)
      {
	 before1 = prevNode;
	 foundNode1 = currentNode;
	 found1 = true;
      }    

      if (currentPosition == secondPosition)
      {
	 before2 = prevNode;
	 foundNode2 = currentNode;
	 found2 = true;
      }    
    
      if (found1 && found2)
	 break;

      prevNode = currentNode;
      currentNode = currentNode->next;
      currentPosition++;
   }
  
   //  cout << "swapping: " << foundNode1->nodeValue << " and " << foundNode2->nodeValue << endl;

   if (firstPosition == 0)
   {
      before2->next = foundNode1;
      node<card> *temp=foundNode2->next;
      firstNode = foundNode2;
      foundNode2->next = foundNode1->next;
      foundNode1->next = temp;
   }
   else if (secondPosition == 0)
   {
      before1->next = foundNode2;
      node<card> *temp=foundNode1->next;
      firstNode = foundNode1;
      foundNode1->next = foundNode2->next;
      foundNode2->next = temp;
   }
   else if (firstPosition - 1 == secondPosition ||
   secondPosition - 1 == firstPosition)
   {
      foundNode1 = foundNode2->next;
      foundNode2 = before1;
   }
   else 
   {
      //copy foundNode1
      node<card> *temp = foundNode1->next;

      before2->next = foundNode1;
      foundNode1->next = foundNode2->next;

      before1->next = foundNode2;
      foundNode2->next = temp;
   }
}

void DeckOfCards::shuffle ()
{
   int totalCards = numberOfCards();
   int r_int1, r_int2;
   randomNumber my_ran;

   for (int shuffleNum = 0; shuffleNum < 1000; shuffleNum++)
   {
      r_int1 = my_ran.random(totalCards);
      r_int2 = my_ran.random(totalCards);
      if (r_int1 == r_int2) 
	 continue;
      swapCards(r_int1, r_int2);
   }

}

card DeckOfCards::dealCard()
{
   card topCard = firstNode->nodeValue;
   delete firstNode;
   firstNode = firstNode->next;
   return topCard;
}

bool DeckOfCards::isEmpty() const
{  
   return (numberOfCards() == 0);
}

ostream& operator<<(ostream& ostr, const DeckOfCards &deck)
//Overloaded print operator
{

   //Keep track of current node
   node<card> *currentNode;
   currentNode = deck.firstNode;

   //While inside the linked list
   while (currentNode != NULL)
   {
      ostr << currentNode->nodeValue << endl;
      //reset the current node
      currentNode = currentNode->next;
   }

   return ostr;
}

